import os, sys, socket, re, zlib
from Cheetah.Template import Template
from plugin import Plugin, quote, unquote, EncodeUnicode
from urlparse import urlparse
from xml.sax.saxutils import escape
from lrucache import LRUCache
from UserDict import DictMixin
from datetime import datetime, timedelta
import config
import metadata
import time
import mind
import logging
import traceback
import shutil
from xml.dom.minidom import parseString
import urllib2
from Cheetah.Filters import Filter

SCRIPTDIR = os.path.dirname(__file__)
CLASS_NAME = 'Playon'
PUSHED = '<h3>Queued for Push to %s</h3> <p>%s</p>'
USERAGENT = "Opera/9.00 (Nintendo Wii; U; ; 1038-58; Wii Shop Channel/1.0; en)"
# Preload the templates
def tmpl(name):
    return file(os.path.join(SCRIPTDIR, 'templates', name), 'rb').read()
killDelayTimeSec = 0
# logger = logging.getLogger('pyTivo')

CONTAINER_TEMPLATE = tmpl('container.tmpl')

class Playon(Plugin):
    CONTENT_TYPE = 'x-container/tivo-videos'    
    videos = {}
    
    def send_file(self, handler, container, name):
        try:   # "bytes=XXX-" 
           range = int(handler.headers.getheader('Range')[6:-1]) 
        except: 
           #print "Range not found" 
           range = 0 
           #print "Range = " + str(range) 
        if (range == 0): 
           # The playon server sends tivo compatible files, so just redirect there
           path = unquote(container) 
           path = path.replace('\\','/') 
           path = path.replace('http:/','http://') 
           path = path.replace('id=','') 
           path = path.replace('localhost',socket.gethostbyname(socket.gethostname())) 
           path = path.replace(':54479',':53478') 
           handler.send_response(302) 
           handler.send_header('Location',path) 
           handler.send_header('Transfer-Encoding','chunked') 
           print "redirecting to: " + path 
           handler.end_headers() 
        else: 
           handler.send_response(206) 
           handler.send_header('Connection', 'close') 
           handler.send_header('Content-Type', 'video/mpeg') 
           handler.send_header('Transfer-Encoding', 'chunked') 
           print "Sending 206 response" 
           handler.end_headers() 
           handler.wfile.write('0\r\n\r\n') 
           handler.wfile.flush() 
        return

    def Delayed_playon_kill(self, killDelayTimeSec):
        if killDelayTimeSec > 0:
            time.sleep(killDelayTimeSec)
            win32serviceutil.StopService("MediaMall Server")
        
    def getVideofolders(self, entry, basepath):
        video = {}
        video['isdir'] = 1
        video['name'] = entry.attributes["name"].value.encode("utf-8")
        #video['name'] = smart_str(entry.attributes["name"].value)
        video['title'] = video['name']
        video['numitems'] = 1
        video['id'] = entry.attributes["href"].value
        video['path'] = entry.attributes["href"].value

        # get count of subitems - is way too slow to be usable at this time.  Commenting out
        #path = basepath + entry.attributes["href"].value
        #req = urllib2.Request(path, None, {'User-agent': USERAGENT})
        #fp = urllib2.urlopen(req)
        #xmlresult = fp.read()
        #fp.close()
        #for groupentry in feed.firstChild.getElementsByTagName("group"):
        #    video['numitems'] = video['numitems'] + 1
        
        return video

    def getVideos(self, feed, basepath):
        videos = []
        for entry in feed.firstChild.getElementsByTagName("group"):
            videos.append(self.getVideofolders(entry,basepath))

        if(len(feed.getElementsByTagName("media"))!=0):
            video = {}
            video['isdir'] = 0
            video['id'] = feed.getElementsByTagName("group")[0].attributes["href"].value
            video['path'] = feed.getElementsByTagName("media")[0].attributes["src"].value

            # the src tag has the url of the actual video, but with an mp4 extension.  That doesn't seem to work, but mpg does
            video['path'] = video['path'].replace('.flv','.mpg')

            isHulu = False
            if video['path'].find('hulu') > -1:
                isHulu = True

            try:
                video['name'] = feed.getElementsByTagName("media")[0].attributes["name"].value.encode("utf-8")
            except IndexError:
                print 'media->name not found:'
                video['name'] = 'Not Found'

            try:
                video['description'] = feed.getElementsByTagName("description")[0].attributes["name"].value.encode("utf-8")
            except IndexError:
                print 'description->name not found:'
                video['description'] = 'No Description'

            try:
                video['title'] = feed.getElementsByTagName("media_title")[0].attributes["name"].value.encode("utf-8")
                
            except IndexError:
                print 'media_title->name not found:'
                video['title'] = 'Not Found'
                
            try:
                video['originalAirDate'] = feed.getElementsByTagName("date")[0].attributes["name"].value
            except IndexError:
                print 'date->name not found:'
                video['originalAirDate'] = 'Not Found'

            try:
                video['time'] = feed.getElementsByTagName("time")[0].attributes["name"].value
            except IndexError:
                print 'time->name not found:'
                video['time'] = '3:30:00'
                

            try:
                video['tvrating'] = feed.getElementsByTagName("rating")[0].attributes["name"].value
                
            except IndexError:
                print 'rating->name not found:'
                video['tvrating'] = 'Not Found'

            video['episodeTitle'] = video['title']
            video['size'] = 10

            durationhours = int(video['time'].split(':')[0])
            durationminutes = int(video['time'].split(':')[1])
            durationseconds = int(video['time'].split(':')[2])
            video['durationhours'] = durationhours
            video['durationminutes'] = durationminutes
            video['durationseconds'] = durationseconds
            
            video['milliseconds'] = (durationhours * 3600000) + (durationminutes * 60000) + (durationseconds * 1000)
            video['duration'] = video['milliseconds'] / 1000
            
            duration_delta = timedelta(milliseconds=int(video['duration'])*1000)

            now = datetime.utcnow()
            video['startTime'] = now.isoformat()
            video['stopTime'] = (now + duration_delta).isoformat()

            min = duration_delta.seconds / 60
            sec = duration_delta.seconds % 60
            hours = min / 60
            min = min % 60
            video['iso_duration'] = 'P' + str(duration_delta.days) + \
                                       'DT' + str(hours) + 'H' + str(min) + \
                                       'M' + str(sec) + 'S'
            size = int(video['duration'])
            if size < 60:
                size = 120 
            elif size < 120:
                size += 90
            elif size < 600:
                size += 60
            elif isHulu:
                size = int(size * 1.2)
            video['size'] = size * 557654
            #video['size'] = int(video['duration']) * 557654
            video['milliseconds'] = int(video['duration']) * 1000
            video['channelnumber'] = ''
            video['channelname'] = ''
            video['seriesId'] = ''
            video['isEpisode'] = 0
            video['playable'] = 1
            videos.append(video)

        return videos

    def item_count(self, query, files, last_start=0):

        totalFiles = len(files)
        index = 0

        if totalFiles and query.has_key('ItemCount'):

            count = int(query['ItemCount'][0])

            if query.has_key('AnchorItem'):
                try:
                    anchor = query['AnchorItem'][0].split('/id=')[1]
                    anchor = unquote(anchor).replace('\\','/')
                    filenames = [x['id'] for x in files]
                    try:
                        index = filenames.index(anchor)
                    except ValueError:
                        print 'Anchor not found:', anchor
                except IndexError:
                    count = 0

                if count > 0:
                    index += 1

                if query.has_key('AnchorOffset'):
                    index += int(query['AnchorOffset'][0])

                #foward count
                if count >= 0:
                    files = files[index:index + count]
                #backwards count
                else:
                    if index + count < 0:
                        count = -index
                    files = files[index + count:index]
                    index += count

            else:  # No AnchorItem

                if count >= 0:
                    files = files[:count]
                else:
                    index = count % len(files)
                    files = files[count:]

        return files, totalFiles, index

    def QueryContainer(self, handler, query):
        tsn = handler.headers.getheader('tsn', '')
        subcname = query['Container'][0]
        cname = subcname.split('/')[0]
        container = handler.server.containers[cname]
        try:
            newpath = subcname.split('/id=')[1]
        except IndexError:
            newpath = '/data/data.xml'
        
        basepath = container.get('path')

        path = basepath + newpath

        # if you don't set the useragent, the src tag won't show at all.  I'm assuming playon is going to serve the
        # correct format based on this.  The Wii seems like a likely to be supported platform
        request = urllib2.Request(path)
        request.add_header('User-Agent',USERAGENT)
        fp = urllib2.urlopen(request)
        xmlresult = fp.read()
        feed = parseString(xmlresult)
        fp.close()
        self.videos, total, start = self.item_count(query, self.getVideos(feed,basepath))

        t = Template(CONTAINER_TEMPLATE, filter=EncodeUnicode)
        t.container = cname
        t.name = subcname 
        t.total = total
        t.start = start
        t.videos = self.videos
        t.quote = quote
        t.escape = escape
        t.crc = zlib.crc32
        t.guid = config.getGUID()
        t.tivo_names = config.tivo_names 
        t.tivos = config.tivos
        t.path = path
        t.playonurl = container.get('path').replace('localhost',socket.gethostbyname(socket.gethostname()))
        handler.send_response(200)
        handler.send_header('Content-Type', 'text/xml')
        handler.send_header('Expires', '0')
        handler.end_headers()
        handler.wfile.write(t)
        return

    def Push(self, handler, query):
        tsn = query['tsn'][0]
        for key in config.tivo_names:
            if config.tivo_names[key] == tsn:
                tsn = key
                break
        tivo_name = config.tivo_names.get(tsn, tsn)

        url = query['File'][0]
        id = quote(url.split('/')[3])
        container = quote(query['Container'][0].split('/')[0])
        ip = config.get_ip(tsn)
        port = config.getPort()
        newurl = 'http://%s:%s/%s/id=%s/%s' % (ip, port, container, quote(url.split('/')[3]), quote(url.split('/')[4]))

        try:
            m = mind.getMind(tsn)
            print 'pushing' + newurl
            m.pushVideo(
                tsn = tsn,
                url = newurl,
                description = self.videos[0]['description'],
                duration = self.videos[0]['duration'],
                size = self.videos[0]['size'],
                title = self.videos[0]['title'],
                subtitle = self.videos[0]['title'],
                source = self.videos[0]['title'],
                mime = 'video/mpeg',
                tvrating = self.videos[0]['description'])
        except Exception, e:
            handler.send_response(500)
            handler.end_headers()
            handler.wfile.write('%s\n\n%s' % (e, traceback.format_exc() ))
            raise

        handler.redir(PUSHED % (tivo_name, newurl), 5)

